package club.gskj.gulimall.shoppingcart.service.impl;

import club.gskj.common.utils.R;
import club.gskj.gulimall.shoppingcart.feign.ProductFeignService;
import club.gskj.gulimall.shoppingcart.interceptor.CartIntercptor;
import club.gskj.gulimall.shoppingcart.service.ICartService;
import club.gskj.gulimall.shoppingcart.vo.CartItemVo;
import club.gskj.gulimall.shoppingcart.vo.CartVo;
import club.gskj.gulimall.shoppingcart.vo.SkuInfoVo;
import club.gskj.gulimall.shoppingcart.vo.UserInfoTo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CartServiceImpl implements ICartService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    /**
     * reids中 存储的购物车key前缀
     */
    public static final String CART_PREFIX = "gulimall:cart:";

    @Override
    public CartItemVo addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
        // 获取操作的redis 购物车(hash)
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();

        //存储商品之前，先判断是否已经存在购物车。
        String product = (String) cartOps.get(skuId.toString());
        // 说明购物车中无此商品信息
        if (StringUtils.isEmpty(product)) {
            // 添加新商品到购物车
            CartItemVo cartItemVo = new CartItemVo();

            // 使用异步编排，
            CompletableFuture<Void> getSkuInfoTask = CompletableFuture.runAsync(() -> {
                // 获取sku信息
                R info = productFeignService.getSkuinfoById(skuId);
                if (info.getCode() == 0) {
                    // sku信息
                    SkuInfoVo skuInfo = info.getData("skuInfo", new TypeReference<SkuInfoVo>() {
                    });
                    // 添加商品到购物车
                    cartItemVo.setCheck(true);
                    cartItemVo.setCount(num);
                    cartItemVo.setImage(skuInfo.getSkuDefaultImg());
                    cartItemVo.setTitle(skuInfo.getSkuTitle());
                    cartItemVo.setSkuId(skuInfo.getSkuId());
                    cartItemVo.setPrice(skuInfo.getPrice());
                }
            }, threadPoolExecutor);

            // 根据skuid 获取销售属性集合
            CompletableFuture<Void> getSkuSaleAttrValuesStringList = CompletableFuture.runAsync(() -> {
                List<String> skuSaleAttrListForString = productFeignService.getSkuSaleAttrListForString(skuId);
                cartItemVo.setSkuAttrs(skuSaleAttrListForString);
            }, threadPoolExecutor);


            // 等待以上两个异步任务完成后，在保存数据到Redis中
            CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(getSkuInfoTask, getSkuSaleAttrValuesStringList);
            // 阻塞等待
            voidCompletableFuture.get();

            // 存储数据到 Redis中
            String s = JSON.toJSONString(cartItemVo);
            cartOps.put(skuId.toString(), s);
            return cartItemVo;
        } else {
            // 商品已存在购物车中，只需要修改商品数量
            CartItemVo original = JSON.parseObject(product, CartItemVo.class);
            // 修改数量
            original.setCount(original.getCount() + num);

            // 存储数据到 Redis中
            String s = JSON.toJSONString(original);
            cartOps.put(skuId.toString(), s);
            return original;
        }
    }

    @Override
    public CartItemVo getCartItem(Long skuId) {

        // 获取操作的redis 购物车(hash)
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        // 从redis中获取 之前添加的商品信息
        String json = (String) cartOps.get(skuId.toString());
        CartItemVo cartItemVo = JSON.parseObject(json, CartItemVo.class);

        return cartItemVo;
    }

    @Override
    public CartVo getCart() throws ExecutionException, InterruptedException {
        CartVo cartVo = new CartVo();

        // 获取用户信息
        UserInfoTo userInfoTo = CartIntercptor.threadLocal.get();
        if (userInfoTo.getUserId() != null) {
            // 用户已登录
            String cartKey = CART_PREFIX + userInfoTo.getUserId();

            // 临时用户购物车数据
            String tmepCartKey = CART_PREFIX + userInfoTo.getUserKey();
            List<CartItemVo> tempUserCartItemVos = getCartItemVos(tmepCartKey);
            if (tempUserCartItemVos != null && tempUserCartItemVos.size() > 0) {
                // 临时购物车有数据，需合并到登陆用户购物车
                for (CartItemVo tempUserCartItemVo : tempUserCartItemVos) {
                    // 合并临时购物车到 已登陆用户购物车中
                    addToCart(tempUserCartItemVo.getSkuId(), tempUserCartItemVo.getCount());
                }
                // 清空临时购物车
                clearCartData(tmepCartKey);
            }
            // 获取合并后的购物车数据[包含合并过的 临时购物车数据，登陆后的购物车数据]
            List<CartItemVo> cartItemVos = getCartItemVos(cartKey);
            cartVo.setItems(cartItemVos);
        } else {
            // 用户未登录
            // 获取临时用户 对应的购物车信息
            String cartKey = CART_PREFIX + userInfoTo.getUserKey();
            List<CartItemVo> cartItemVos = getCartItemVos(cartKey);
            cartVo.setItems(cartItemVos);
        }
        return cartVo;
    }

    @Override
    public void clearCartData(String key) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        // 删除 redis 中购物车信息
        stringRedisTemplate.delete(key);
    }

    @Override
    public void checkItem(Long skuId, Integer check) {
        CartItemVo cartItem = getCartItem(skuId);
        // 修改选中状态
        cartItem.setCheck(check == 1);

        String s = JSON.toJSONString(cartItem);
        // 重新放入 redis中
        getCartOps().put(skuId.toString(), s);
    }

    @Override
    public void countItem(Long skuId, Integer num) {
        CartItemVo cartItem = getCartItem(skuId);
        cartItem.setCount(num);
        String s = JSON.toJSONString(cartItem);
        // 重新放入 redis中
        getCartOps().put(skuId.toString(), s);
    }

    @Override
    public void removeItem(Long skuId) {
        getCartOps().delete(skuId.toString());
    }

    @Override
    public List<CartItemVo> getUserCartItems() {
        // 获取登陆用户信息
        UserInfoTo userInfoTo = CartIntercptor.threadLocal.get();
        if (userInfoTo.getUserId() == null) {
            return null;
        }
        String cartKey = CART_PREFIX + userInfoTo.getUserId();

        List<CartItemVo> cartItemVos = getCartItemVos(cartKey);
        // 只过滤用户选中的 购物项
        List<CartItemVo> collect = cartItemVos.stream()
                .map(item -> {
                    // 每次都要查询数据库中的最新价格
                    R r = productFeignService.getPrice(item.getSkuId());
                    if (r.getCode() == 0) {
                        item.setPrice(new BigDecimal(r.getData("data", new TypeReference<String>() {
                        })));
                    }
                    return item;
                })
                .filter(item -> {
                    return item.getCheck();
                }).collect(Collectors.toList());
        return collect;
    }

    /**
     * 获取redis中要操作的购物车数据
     */
    private BoundHashOperations<String, Object, Object> getCartOps() {
        // 获取用户信息
        UserInfoTo userInfoTo = CartIntercptor.threadLocal.get();
        StringBuffer key = new StringBuffer("");
        if (userInfoTo.getUserId() != null) {
            // 用户已登录
            key.append(CART_PREFIX).append(userInfoTo.getUserId());
        } else {
            // 用户未登录
            key.append(CART_PREFIX).append(userInfoTo.getUserKey());
        }
        // 添加商品到 购物车，如果购物车中已有商品。只改变商品数量
        BoundHashOperations<String, Object, Object> stringObjectObjectBoundHashOperations = stringRedisTemplate.boundHashOps(key.toString());
        return stringObjectObjectBoundHashOperations;
    }

    /***
     *  根据 key 获取用户对应的购物车数据
     * @param cartKey key值
     */
    private List<CartItemVo> getCartItemVos(String cartKey) {
        BoundHashOperations<String, Object, Object> ops = stringRedisTemplate.boundHashOps(cartKey);
        List<Object> values = ops.values();
        if (values != null && values.size() > 0) {
            List<CartItemVo> cartItemVos = values.stream().map(item -> {
                String json = (String) item;
                CartItemVo cartItemVo = JSON.parseObject(json, CartItemVo.class);
                return cartItemVo;
            }).collect(Collectors.toList());
            return cartItemVos;
        }
        return null;
    }
}
